﻿using GTMC.Enum;
using GTMC.Interface.Communi;
using GTMC.Model;
using Leeder.GTMC.Communi;
using Leeder.Util;
using Leeder.Logger;
using LeederSerialPort;
using NewAndong.Business;
using NewAndong.Comm;
using NewAndong.Enum;
using NewAndong.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Andong.Model;
using Newtonsoft.Json;
using LEDDisplay.SDK;
using System.Reflection;
using Sunny.UI.Win32;
using System.Xml.Linq;
using System.Configuration;

namespace NewAndong.Handle
{
    public class NewAndongHandler
    {
        /// <summary>
        /// 日志
        /// </summary>
        public LogOperate LogObj => Log.GetLogger(this.GetType());
        /// <summary>
        /// 主要设备监控线程
        /// </summary>
        public CancellationTokenSource cancel_mainequip;
        /// <summary>
        /// 立库发送线程
        /// </summary>
        public CancellationTokenSource cancel_tcpSend;
        /// <summary>
        /// 
        /// </summary>
        public CancellationTokenSource cancel_picsQueue;
        /// <summary>
        /// 丰田工机数据读取线程
        /// </summary>
        public CancellationTokenSource cancel_toyopucRead;
        /// <summary>
        /// 丰田工机状态监控线程
        /// </summary>
        public CancellationTokenSource cancel_TriaState;

        TcpIpServer tcpIpServer;

        private MaterialDirectionBLL bllMaterialDirection = new MaterialDirectionBLL();
        private SystemDeviceMonitorBLL bllSystemDeviceMonitor = new SystemDeviceMonitorBLL();
        private TriaConnectBLL triaConnectBLL = new TriaConnectBLL();
        private TriaGroupBLL triaGroupBLL = new TriaGroupBLL();
        private TriaItemBLL triaItemBLL = new TriaItemBLL();
        private SignalLampMappingBLL signalLampMappingBLL = new SignalLampMappingBLL();
        private SignalLampDirectionBLL signalLampDirectionBLL = new SignalLampDirectionBLL();
        private UnpackingDirectionBLL unpackingDirectionBLL = new UnpackingDirectionBLL();
        private SignalLampDirectionBLL bllSignalLampDirection = new SignalLampDirectionBLL();
        private UnpackingDirectionBLL bllUnpackingDirection = new UnpackingDirectionBLL();
        private SystemDeviceBLL bllSystemDevice = new SystemDeviceBLL();
        private DirectQuoteBLL bllDirectQuote = new DirectQuoteBLL();
        private WmsDataBLL wmsDataBLL = new WmsDataBLL();
        private PlanMonitorBLL bllLogisticsSystemPlanMonitor = new PlanMonitorBLL();
        /// <summary>
        /// 定义ups服务变量
        /// </summary>
        private UpsHandle upsServer;
        /// <summary>
        /// 顺引顺建剩余数量扣减事件
        /// </summary>
        public event Action LipChangeEvent;

        /// <summary>
        /// 设备状态改变事件
        /// </summary>
        public event Action<SystemDeviceModel> OnDeviceStateChanged;
        /// <summary>
        /// UPS状态变化事件
        /// </summary>
        public event Action<UpsModel> UpsChangeEvent;

        AutoResetEvent atresetEvent = new AutoResetEvent(false);

        private class WriteStruct
        {
            public IToyopuc Toyopuc;
            public ProgramCode Code;
            public IndirectAddress Address;
            public List<byte> Val;
        }

        private NewAndongHandler() { }
        private static NewAndongHandler _NewAndongBusiness = null;
        public static NewAndongHandler CreateInstance()
        {
            if (_NewAndongBusiness == null)
            {
                _NewAndongBusiness = new NewAndongHandler();
            }
            return _NewAndongBusiness;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            InitData();
            InitToyopuc();
            InitTcp();
            InitTask();
            ToyopucMonitor();      
        }

        /// <summary>
        /// 初始化数据
        /// </summary>
        private void InitData()
        {
            LogObj.Info("初始化系统参数");
            var timeout = ConfigurationManager.AppSettings["timeout"].ToString();
            GolbalVariable.TimeOut = int.Parse(timeout);

            //从数据库中取出系统需要的数据
            List<MaterialDirectionModel> materialDirectionList = bllMaterialDirection.GetModelList();
            List<SignalLampDirectionModel> signalLampDirectionList = bllSignalLampDirection.GetModelList();
            List<UnpackingDirectionModel> unpackingDirectionList = bllUnpackingDirection.GetModelList();
            List<DirectQuoteModel> directQuoteList = bllDirectQuote.GetModelList();
            List<SystemDeviceModel> systemDeviceList = bllSystemDevice.GetModelList();

            materialDirectionList.ForEach(x => { x.UpdateTime = DateTime.Now.AddSeconds(-30); x.LastValue=0 ; });
            //初始化全局变量
            GolbalVariable.PicsQueue = new ConcurrentQueue<PicsQueueModel>();
            GolbalVariable.SignalLampMappingList = signalLampMappingBLL.GetModelAll();
            GolbalVariable.MaterialDirectionBag = new ConcurrentBag<MaterialDirectionModel>(materialDirectionList);
            GolbalVariable.SignalLampDirectionBag = new ConcurrentBag<SignalLampDirectionModel>(signalLampDirectionList);
            GolbalVariable.SignalLampDirectionBag.ForEach(x => { x.Status = 0; });//初始化全部信号灯状态为0
            GolbalVariable.UnpackingDirectionBag = new ConcurrentBag<UnpackingDirectionModel>(unpackingDirectionList);
            GolbalVariable.DirectQuoteSettingBag = new ConcurrentBag<DirectQuoteModel>(directQuoteList);
            GolbalVariable.SystemDeviceBag = new ConcurrentBag<SystemDeviceModel>(systemDeviceList);
            GolbalVariable.SignalLampBlinkThreadDictionary = new ConcurrentDictionary<string, Thread>();
            GolbalVariable.SignalLampShowModelBag = new ConcurrentBag<SignalLampShowModel>();
            GolbalVariable.UnpackingDirectionModelBag = new ConcurrentBag<UnpackingDirectionShowModel>();
            GolbalVariable.CurrentParamet = new CurrentParameter();
            GolbalVariable.UpsStatus = new UpsModel();
            GolbalVariable.LipState = true;
            GolbalVariable.PlcState = true;

            CurrentParameter currentParamet = new CurrentParameter();
            GolbalVariable.CurrentParamet = currentParamet;

            //初始化全局变量
            foreach (var signalLamp in signalLampDirectionList)
            {
                var unpacking = unpackingDirectionList.Where(p => p.ID == signalLamp.UnpackingId).FirstOrDefault();
                var signalLampAddress = GolbalVariable.SignalLampMappingList.Where(s => s.ID == signalLamp.SignalLampMappingID).FirstOrDefault();
                if (unpacking != null)
                {
                    SignalLampShowModel model = new SignalLampShowModel
                    {
                        Id = signalLamp.ID,
                        LampId = signalLamp.ID,
                        PullingRopeName = signalLamp.LampName,
                        ChainMark = signalLamp.ChainMark,
                        PullingRopeInAddress = signalLampAddress.ControlInputAddress,
                        GreenOutAddress = signalLampAddress.GreenOutputAddress,
                        RedOutAddress = signalLampAddress.RedOutputAddress,
                        TypeId = signalLamp.TypeId
                    };
                    GolbalVariable.SignalLampShowModelBag.Add(model);
                }
            }

            //初始化全局变量
            foreach (var unpacking in unpackingDirectionList)
            {
                UnpackingDirectionShowModel model = new UnpackingDirectionShowModel
                {
                    Id = unpacking.ID,
                    UnpackingId = unpacking.ID,
                    UnpackingName = unpacking.UnpackingName,
                    DataType = unpacking.DataType,
                    IpAddress = unpacking.IpAddress,
                    SignalMark = unpacking.SignalMark,
                    ControlName = unpacking.ControlName,
                    TypeId = unpacking.TypeId
                };
                GolbalVariable.UnpackingDirectionModelBag.Add(model);
            }
            LogObj.Info("系统参数初始化完成");
        }

        /// <summary>
        /// 初始化丰田工机
        /// </summary>
        public void InitToyopuc()
        {
            LogObj.Info("初始化丰田工机");
            try
            {
                GolbalVariable.TriaConnectList = triaConnectBLL.GetModelList();
                GolbalVariable.TriaGroupList = triaGroupBLL.GetModelList();
                GolbalVariable.TriaItemList = triaItemBLL.GetModelList();
                GolbalVariable.PlcTriaList = new List<TriaModel>();
                foreach (var conn in GolbalVariable.TriaConnectList)
                {
                    TriaModel triaModel = new TriaModel();
                    GolbalVariable.PlcTriaList.Add(triaModel);
                    triaModel.ToyopucHandler = ToyopucInstance.GetInstance(conn.Ip, conn.Port);
                    triaModel.Ip = conn.Ip;
                    triaModel.Port = conn.Port;
                    triaModel.AccessMode = conn.AccessMode;
                    triaModel.ProgramCode = (ProgramCode)conn.ProgramCode;
                    triaModel.TriaAddress = new IndirectAddress();
                    triaModel.TriaAddress.Identifier = conn.Identifier;
                    triaModel.TriaAddress.Address = (uint)conn.Address;
                    triaModel.AddressLength = (short)conn.AddressLength;
                    triaModel.TriaGroups = new List<TriaGroup>();
                    for (uint i = triaModel.TriaAddress.Address; i < (triaModel.TriaAddress.Address + triaModel.AddressLength); i++)
                    {
                        var groupId = triaModel.TriaAddress.Identifier + i.ToString("X");
                        var group = GolbalVariable.TriaGroupList.Where(s => s.GroupAddress == groupId && s.TriaConnectID == conn.ID).FirstOrDefault();
                        if (group == null)
                            continue;
                        TriaGroup triaGroup = new TriaGroup
                        {
                            GroupId = groupId,
                            GroupDataType = group.GroupType == 0 ? TriaDataType.Booleans : TriaDataType.Integer,
                            Value = -1,
                            UpdateTime = DateTime.Now.AddSeconds(-30),
                            LastValue = -1,
                            Items = new List<TriaItem>()
                        };
                        triaModel.TriaGroups.Add(triaGroup);
                        switch (triaGroup.GroupDataType)
                        {
                            case TriaDataType.Integer:
                                triaGroup.Items.Add(new TriaItem
                                {
                                    ItemId = triaGroup.GroupId + "_0"
                                });
                                break;
                            case TriaDataType.Booleans:
                                for (int j = 0; j < 16; j++)
                                {
                                    triaGroup.Items.Add(new TriaItem
                                    {
                                        ItemId = triaGroup.GroupId + "_" + j.ToString("X")
                                    });
                                }
                                break;
                        }
                    }
                }
                LogObj.Info("丰田工机初始化完成");
            }
            catch (Exception ex)
            {
                LogObj.Error("丰田工机初始化失败：" + ex.Message);
            }
        }

        /// <summary>
        /// 初始化线程
        /// </summary>
        private void InitTask()
        {
            //主要设备监控线程
            if (GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").Any())
            {
                cancel_mainequip = new CancellationTokenSource();
                LogObj.Info("开启主要设备监控线程启动");
                Task.Factory.StartNew(() =>
                {
                    BindMainPower();
                }, cancel_mainequip.Token);
            }
            //丰田工机数据监控线程(读取数据，判断是否有按钮按下)
            cancel_toyopucRead = new CancellationTokenSource();
            LogObj.Info("丰田工机信号监控线程启动");
            Task.Factory.StartNew(() =>
            {
                ToyopucRead();
            }, cancel_toyopucRead.Token);

            //开启PICS数据处理工作线程
            cancel_picsQueue = new CancellationTokenSource();
            LogObj.Info("PICS数据处理线程启动");
            Task.Factory.StartNew(() =>
            {
                PicsQueue();
            }, cancel_picsQueue.Token);

            cancel_tcpSend = new CancellationTokenSource();
            LogObj.Info("立库数据推送线程启动");
            Task.Factory.StartNew(() =>
            {
                TcpSend();
            }, cancel_tcpSend.Token);

            //创建ups监控对象的实例
            upsServer = new UpsHandle();
            //接收数据事件
            upsServer.Received += UpsServer_Received;
            //初始化com口
            LogObj.Info("ups监控线程开启");
            upsServer.InitCom();
        }

        /// <summary>
        /// 初始化TCP服务（目前用于向立库推送出发信息）
        /// </summary>
        private void InitTcp()
        {
            // 创建TCP服务器实例并初始化
            tcpIpServer = new TcpIpServer("172.16.157.14", 8899);

            string msg = "";
            // 启动服务器监听
            if (tcpIpServer.Bind(ref msg))
            {
                LogObj.Info("TCP服务启动成功");
            }
            else
            {
                LogObj.Error($"TCP服务启动失败->{msg}");
            }
        }

        /// <summary>
        /// 绑定硬件设备
        /// </summary>
        private async void BindMainPower()
        {
            var pingList = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").ToList();
            foreach (var item in pingList) { item.Status = -1; }; //初始化时，把需要ping的设备状态全部设为-1，即未知状态

            //软件重启，初始化开屏或关屏
            Thread thread = new Thread(() =>
            {
                foreach (var item in pingList)
                {
                    if (item.IsOpen)
                        OpenLed(item,false);
                    else
                        CloseLed(item,false);
                }
            });
            thread.IsBackground = true;
            thread.Start();


            while (true)
            {
                if (cancel_mainequip.Token.IsCancellationRequested)
                {
                    cancel_mainequip.Dispose();
                    break;
                }
                foreach (var item in pingList)
                {
                    var result = PublicHelper.PingIp(item.DeviceIpAddress);
                    //ping失败，继续最多ping5次
                    if (!result)
                    {
                        for (int i = 0; i < 5; i++)
                        {
                            result = PublicHelper.PingIp(item.DeviceIpAddress);
                            if (result)
                                break;
                        }
                    }

                    if (result && item.Status != 1)
                    {
                        item.Status = 1;
                        OnDeviceStateChanged?.BeginInvoke(item, null, null);
                    }
                    else if (!result && item.Status != 0)
                    {
                        item.Status = 0;
                        OnDeviceStateChanged?.BeginInvoke(item, null, null);

                        var deviceId = !string.IsNullOrEmpty(item.DeviceCode) ? (!item.DeviceCode.ToLower().StartsWith("led")?item.DeviceName: item.DeviceCode) : item.DeviceName;
                        SystemDeviceMonitorModel model = new SystemDeviceMonitorModel()
                        {
                            ID = item.ID,
                            DeviceId = deviceId,
                            DeviceName = item.DeviceName,
                            ExceptionDescription = "设备通讯异常",
                            ExceptionDate = DateTime.Now
                        };
                        bllSystemDeviceMonitor.Add(model);
                    }
                }
                await Task.Delay(60000);
            }
        }

        /// <summary>
        /// 丰田工机通讯监控
        /// </summary>
        public void ToyopucMonitor()
        {
            Thread thread = new Thread(() =>
            {
                while (true)
                {
                    bool re = atresetEvent.WaitOne(60000);//等60秒，60秒没有信号则取消旧线程重新创建读取线程
                    if (!re)
                    {
                        LogObj.Error("丰田工机通讯异常，无法读取数据！");
                        //丰田工机数据监控线程(读取数据，判断是否有按钮按下)
                        cancel_toyopucRead.Cancel();
                        Thread.Sleep(300);
                        cancel_toyopucRead = new CancellationTokenSource();
                        Task.Factory.StartNew(() =>
                        {
                            InitToyopuc();
                            ToyopucRead();
                        }, cancel_toyopucRead.Token);
                    }
                }
            }
            );
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// 实时读取丰田Toyopuc数据
        /// </summary>
        private async void ToyopucRead()
        {
            while (!this.cancel_toyopucRead.Token.IsCancellationRequested)
            {
                //遍历所有连接
                foreach (var plcTria in GolbalVariable.PlcTriaList)
                {
                    await Task.Delay(20);
                    //如果连接
                    if (plcTria.AccessMode == "writeOnly")
                        continue;
                    var toyopucHandler = plcTria.ToyopucHandler;
                    if (toyopucHandler == null) continue;
                    byte[] result = null;
                    try
                    {
                        //await Task.Delay(10000);//测试代码

                        //查询当前按钮的状态(一次读多个地址的数据)
                        result = toyopucHandler.ReadExtString(plcTria.ProgramCode, plcTria.TriaAddress, plcTria.AddressLength);// result = PublicHelper.ReadPlcDataTest(plcTria);//测试

                        if (result.Length > 0)
                            atresetEvent.Set();
                    }
                    catch (Exception ex)
                    {
                        LogObj.Error("PLC数据读取异常:" + ex.Message);
                        continue;
                    }
                    //将读到的字节转换成二进制字符串
                    string resultStr = PublicHelper.ByteToStr(result);
                    //比较当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                    if (plcTria.DataStr != resultStr)
                    {
                        plcTria.DataByte = result;
                        plcTria.DataStr = resultStr;
                        if (string.IsNullOrEmpty(resultStr))
                            continue;

                        int packetIndex = 0;

                        //每个地址分成高8位和低8位
                        byte byte1 = 0;
                        byte byte2 = 0;

                        //循环判断每一组数据
                        for (uint i = plcTria.TriaAddress.Address; i < (plcTria.TriaAddress.Address + plcTria.AddressLength); i++)
                        {
                            try
                            {
                                //根据组ID获取该组
                                var triaGroup = plcTria.TriaGroups.Where(s => s.GroupId == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();
                                if (triaGroup == null)
                                {
                                    var states = resultStr.FindIndexString(ref packetIndex, 16);
                                    continue;
                                }
                                //截取该组数据(转换后的二进制字符串)
                                var groupStates = resultStr.FindIndexString(ref packetIndex, 16);

                                //比较该组当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                                if (triaGroup.GroupStrs != groupStates)
                                {
                                    triaGroup.LastValue = triaGroup.Value;
                                    triaGroup.Value = BitConverter.ToUInt16(result, (int)((i - plcTria.TriaAddress.Address) * 2));

                                    try
                                    {
                                        //拆分成高8位和低8位
                                        byte1 = result[(int)((i - plcTria.TriaAddress.Address) * 2)];
                                        byte2 = result[(int)((i - plcTria.TriaAddress.Address) * 2 + 1)];
                                    }
                                    catch (Exception ex)
                                    {
                                        LogObj.Error($"byte1,byte2 取值失败->{ex.ToString()}");
                                    }

                                    triaGroup.GroupStrs = groupStates;
                                    var group = GolbalVariable.TriaGroupList.Where(s => s.GroupAddress == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();
                                    var items = GolbalVariable.TriaItemList.Where(s => s.GroupID == group.ID).ToList();

                                    //根据组名分别调用不同的逻辑方法
                                    switch (triaGroup.GroupDataType)
                                    {
                                        case TriaDataType.Integer:
                                            //比较当前项读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                                            if (triaGroup.Items[0].Value == null || (triaGroup.Items[0].Value != triaGroup.Value))
                                            {
                                                //将读到的二进制数转成十进制数并更新到数据模型中
                                                triaGroup.Items[0].Value = triaGroup.Value;
                                                LogObj.Info("Item(Word)信号触发(" + group.GroupAddress + "):" + triaGroup.Items[0].Value + "(" + triaGroup.GroupStrs + ")");

                                                //根据组名分别调用不同的逻辑方法
                                                switch (group.SignalMark)
                                                {
                                                    case "LIP"://获取产线下线台数

                                                       HandlerError(triaGroup);
                                                        if (triaGroup.LastValue.ToInt() != -1 && triaGroup.Value.ToInt() != 0)
                                                        {
                                                            for (int k = 0; k < ((triaGroup.Value.ToInt() - triaGroup.LastValue.ToInt())); k++)
                                                            {
                                                                LipGroup(items[0].SignalMark, 1, triaGroup);
                                                                await Task.Delay(10);
                                                            }
                                                        }
                                                        break;
                                                    case "TS"://特殊
                                                        HandlerError(triaGroup);

                                                        if (triaGroup.Value.ToInt() == 0 && triaGroup.LastValue.ToInt() != -1)
                                                        {
                                                            LipGroup(items[0].SignalMark, 1, triaGroup);
                                                        }
                                                        else if (triaGroup.LastValue.ToInt() != -1)
                                                        {
                                                            for (int k = 0; k < ((triaGroup.Value.ToInt() - triaGroup.LastValue.ToInt())); k++)
                                                            {
                                                                LipGroup(items[0].SignalMark, 1, triaGroup);
                                                                await Task.Delay(10);
                                                            }
                                                        }
                                                        break;
                                                    case "LSD"://拉绳灯
                                                        break;
                                                    case "KK"://开捆
                                                        break;
                                                    case "RD"://日调
                                                        RdGroup(items[0].SignalMark, Convert.ToInt32(triaGroup.Items[0].Value));
                                                        break;
                                                    case "SBJK"://设备监控
                                                        break;
                                                    case "PICS"://开捆
                                                        //业务处理
                                                        try
                                                        {
                                                            LogObj.Info("PICS触发->" + items[0].SignalMark);
                                                            GolbalVariable.PicsQueue.Enqueue(new PicsQueueModel { SignalMark = items[0].SignalMark, Result = PublicHelper.ByteToInt(byte1) });
                                                        }
                                                        catch (Exception ex) { }
                                                        break;
                                                    case "LSZT":
                                                        break;
                                                }
                                            }
                                            break;
                                        case TriaDataType.Booleans:
                                            for (int j = 0; j < 16; j++)
                                            {
                                                //比较当前项读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                                                if (triaGroup.Items[j].Value == null || (triaGroup.Items[j].Value.ToInt() != int.Parse(groupStates[j].ToString())))
                                                {
                                                    var val = triaGroup.Items[j].Value;
                                                    LogObj.Info("Item(Bit)信号触发(" + group.GroupAddress + "_" + j + "):" + val + "(" + triaGroup.Items[j].Value.ToInt() + ")");
                                                    //将读到的二进制数转成单个0或1并更新到数据模型中
                                                    triaGroup.Items[j].Value = int.Parse(groupStates[j].ToString());
                                                    var item = items.Where(s => s.ItemAddress == triaGroup.Items[j].ItemId).FirstOrDefault();
                                                    //如果信号配置为空，则跳过并进入下一个循环
                                                    if (item == null) continue;
                                                    //根据组名分别调用不同的逻辑方法
                                                    switch (group.SignalMark)
                                                    {
                                                        case "LIP"://获取产线下线台数
                                                            LipGroup(item.SignalMark, triaGroup.Items[j].Value.ToInt(), triaGroup);
                                                            break;
                                                        case "LSD"://拉绳灯
                                                            break;
                                                        case "KK"://开捆
                                                            break;
                                                        case "RD"://日调
                                                            break;
                                                        case "SBTX"://设备监控
                                                            SbjkGroup(item.SignalMark, triaGroup.Items[j].Value.ToInt());
                                                            break;
                                                        case "PICS"://开捆
                                                            break;
                                                        case "TS"://特殊
                                                            if (val != null)
                                                                LipGroup(item.SignalMark, triaGroup.Items[j].Value.ToInt(), triaGroup);
                                                            break;
                                                    }
                                                }
                                            }
                                            break;
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                LogObj.Error(ex.Message);
                                break;
                            }
                        }
                    }
                }
                await Task.Delay(100);
            }
        }

        /// <summary>
        /// 处理跳数异常
        /// </summary>
        /// <param name="triaGroup"></param>
        private static void HandlerError(TriaGroup triaGroup)
        {
            var now = DateTime.Now;
            //跟上次变化的时间小于20秒，跳过不处理
            if (triaGroup.UpdateTime != null && now.Subtract(triaGroup.UpdateTime).TotalSeconds < GolbalVariable.TimeOut)
                return;

            triaGroup.UpdateTime = now;

            if (triaGroup.Value.ToInt()>0 && triaGroup.Value.ToInt() < triaGroup.LastValue.ToInt())
            {
                var sub = triaGroup.Value.ToInt() - triaGroup.LastValue.ToInt();
                //相差1到3个数
                if(sub==-1 || sub==-2 || sub==-3)
                {
                    triaGroup.Value = triaGroup.LastValue;
                    triaGroup.Items[0].Value = triaGroup.LastValue;
                }

            }
        }

        /// <summary>
        /// UPS接收数据事件
        /// </summary>
        /// <param name="data"></param>
        private void UpsServer_Received(UpsModel data)
        {
            try
            {
                LogObj.Debug(DateTime.Now + ": 输入电压:" + data.InputVoltage + "  输出电压:" + data.OutputVoltage + "  负载:" + data.Load + "  温度:" + data.Temperature + "  负载:" + data.Load);
                //把UPS当前状态更新到缓存中（用于界面状态的更新）
                GolbalVariable.UpsStatus = data;
                //数据处理
                UpsBusiness.OnExceptionEvent += UpsEvent;
                UpsBusiness.CheckUps(data);
                UpsChangeEvent?.BeginInvoke(GolbalVariable.UpsStatus,null, null);
            }
            catch (Exception ex)
            {
                LogObj.Error(ex.ToString());
            }
        }

        public void UpsEvent(string mesg)
        {
            LogObj.Error(mesg);
        }

        /// <summary>
        /// 物料数量扣减和重置
        /// </summary>
        public void LipGroup(string signalMark, int result,TriaGroup triaGroup)
        {
            Task.Run(() => {
                if (result == 0)
                    return;

                //把OPC触发点对应的部品装进队列里，以供另外一个线程读取并做相应的运算(有备用两个字的工程不计算剩余台数)
                var plans = GolbalVariable.MaterialDirectionBag.Where(x => x.SignalMark == signalMark&&!x.MaterialName.Contains("备用")).ToList();
                if (plans.Any())
                {

                    foreach(var x in plans)
                    {
                        Task.Run(() =>
                        {
                            Update(x, result);
                            Thread.Sleep(20);
                        });
                    }
                }
            });
        }

        private void Update(MaterialDirectionModel x,int result)
        {
            try
            {
                //计算方法
                int left_count = x.LeftCount;
                CalculationHelper.Subtraction(ref left_count, 1);
                //如果剩余台数小于1，则剩余台数等于工程台数,开始新的一轮循环
                var res = CalculationHelper.CustomOperation(ref left_count, 3, 1, x.ProjectCount);
                //如果部品开始新的一轮循环且部品类型为内制品则出发工程数减1
                if (res && x.TypeId == 4)
                {
                    int project_count = x.TransportProjectCount;
                    CalculationHelper.Subtraction(ref project_count, 1);
                    CalculationHelper.CustomOperation(ref project_count, 3, 1, x.TriggerProjectCount);
                    x.TransportProjectCount = project_count;
                }
                x.LeftCount = left_count;

                if (x.TypeName.Equals("大物"))
                {
                    var signalLamp = signalLampDirectionBLL.GetModelByNameOrSpecMark(x.MaterialName).FirstOrDefault();
                    if (signalLamp != null)
                    {
                        LogObj.Info($"{x.MaterialName}->延迟数量计算");
                        try
                        {
                            //计算实际延迟台份
                            var realDelayCount = x.StartTransportCount - left_count;
                            x.RealDelayCount = realDelayCount > 0 ? realDelayCount.ToString() : "";

                            if (res)
                            {
                                signalLamp.DeliveryStatus = 0;
                            }
                            if (-signalLamp.TriggerDelayCount <= realDelayCount && realDelayCount <= signalLamp.TriggerDelayCount && signalLamp.DeliveryStatus == 0)
                            {
                                signalLamp.Status = 2;
                                signalLamp.RealDelayCount = "";
                                signalLamp.Qty = x.LeftCount;
                            }
                            else if (realDelayCount > signalLamp.TriggerDelayCount && signalLamp.DeliveryStatus == 0)
                            {
                                signalLamp.RealDelayCount = realDelayCount.ToString();
                                signalLamp.Status = 1;
                                signalLamp.Qty = x.LeftCount;

                                //找到对应的开捆
                                var unpacking = GolbalVariable.UnpackingDirectionModelBag.Where(s => s.UnpackingName == signalLamp.TypeName && s.DataType == "台数").FirstOrDefault();

                                if (unpacking != null)
                                {
                                    var UnpackLs = unpackingDirectionBLL.GetById(unpacking.Id).ActivedObj;
                                    if (UnpackLs != null)
                                        RecordDelay(signalLamp, UnpackLs);
                                }
                            }
                            else
                            {
                                signalLamp.RealDelayCount = "";
                                signalLamp.Status = 0;
                                signalLamp.Qty = x.LeftCount;
                            }
                            var updatesql = string.Format("update LA_SignalLampDirection set DeliveryStatus={0},Status={1},RealDelayCount={2},Qty={3} where ID={4}", signalLamp.DeliveryStatus, signalLamp.Status, string.IsNullOrEmpty(signalLamp.RealDelayCount) ? "''" : signalLamp.RealDelayCount, signalLamp.Qty, signalLamp.ID);
                            signalLampDirectionBLL.UpdateNum(updatesql);
                            //signalLampDirectionBLL.Update(signalLamp);
                        }
                        catch (Exception ex)
                        {
                            LogObj.Error("计算延迟数异常->" + ex.ToString());
                        }
                    }
                }

                //把运算后的部品实体更新到数据库
                var sql = string.Format("update LA_MaterialDirection set TransportProjectCount={0},LeftCount={1},RealDelayCount={2} where ID={3}", x.TransportProjectCount, x.LeftCount, string.IsNullOrEmpty(x.RealDelayCount) ? "''" : x.RealDelayCount, x.ID);
                bllMaterialDirection.UpdateNum(sql);
                //bllMaterialDirection.Update(x);
                LipChangeEvent?.Invoke();
                LogObj.Info("顺引顺建/内制品/排序工程剩余台数扣减(" + x.MaterialName + ":" + x.LeftCount + ")");
            }
            catch(Exception ex)
            {
                LogObj.Error(ExceptionType.ServiceException,ex,"计算剩余数量出错");
            }
        }

        /// <summary>
        /// 状态转换
        /// </summary>
        /// <param name="stateCode"></param>
        /// <returns></returns>
        public string GetState(int stateCode)
        {
            switch (stateCode)
            {
                case 0:
                    //0状态(常态)下拉绳不作处理
                    return "常态";
                case 1:
                    //1状态(提示配送)下拉绳信号灯变成3状态(配送状态)
                    return "提示配送";
                case 2:
                    //2状态(未在规定时间内配送)下拉绳信号灯变成3状态(配送状态)
                    return "未在规定时间内开始配送";
                case 3:
                    //3状态(配送状态)下拉绳信号灯变成0状态(配送结束)
                    return "配送状态";
                case 4:
                    //4状态(未在规定时间内完成配送)下拉绳信号灯变成0状态(配送结束)
                    return "未在规定时间内完成配送";
                default:
                    return "未定义状态";
            }
        }

        /// <summary>
        /// SBJK处理逻辑
        /// 监控硬件设备的状态并同步更新缓存，OFF为异常，NO为正常
        /// </summary>
        public void SbjkGroup(string signalMark, int result)
        {
            var systemDevices = GolbalVariable.SystemDeviceBag.Where(x => x.SignalMark == signalMark && x.Enabled).ToList();
            systemDevices.ForEach(x =>
            {
                //设备异常时把异常信息保存到数据库，触发前状态已经是异常的不做记录(连续异常只记录第一次)
                if (result == 0 && x.Status != result)
                {
                    LogObj.Error("设备通讯异常:" + x.DeviceName);
                    SystemDeviceMonitorModel model = new SystemDeviceMonitorModel()
                    {
                        DeviceId = x.DeviceCode.ToString(),
                        DeviceName = x.DeviceName,
                        ExceptionDescription = "设备通讯异常",
                        ExceptionDate = DateTime.Now
                    };
                    bllSystemDeviceMonitor.Add(model);
                }
                //更新设备缓存状态
                x.Status = result;
            });
        }

        /// <summary>
        /// PLC RD组处理逻辑
        /// </summary>
        public void RdGroup(string signalMark, int result)
        {
            //链数/台数更新
            var unpackings = GolbalVariable.UnpackingDirectionModelBag.Where(x => x.SignalMark == signalMark).ToList();
            if (unpackings != null)
                foreach (UnpackingDirectionShowModel x in unpackings)
                {
                    x.UnpackingCount = result;
                    var model = unpackingDirectionBLL.GetById(x.Id).ActivedObj;
                    model.Amount = result;
                    unpackingDirectionBLL.Update(model);
                }
        }

        /// <summary>
        /// Pics队列数据处理
        /// </summary>
        private async void PicsQueue()
        {
            while (!this.cancel_picsQueue.Token.IsCancellationRequested)
            {
                if (!GolbalVariable.PicsQueue.IsEmpty)
                {
                    PicsQueueModel result;
                    GolbalVariable.PicsQueue.TryDequeue(out result);
                    if (result != null)
                    {
                        LogObj.Info("Pics队列数据处理->" + result.SignalMark);
                        PicsGroup(result.SignalMark, result.Result);
                    }
                    await Task.Delay(20);
                }
                else
                {
                    await Task.Delay(100);
                }
            }
        }

        /// <summary>
        /// PICS处理逻辑
        /// </summary>
        public void PicsGroup(string signalMark, int result)
        {
            //链数/台数更新
            var unpackings = GolbalVariable.UnpackingDirectionModelBag.Where(x => x.SignalMark == signalMark).ToList();
            if (unpackings != null)
            {
                List<SignalLampDirectionModel> signalLampDirectionList = bllSignalLampDirection.GetModelList();
                ConcurrentBag<SignalLampDirectionModel> signalLampDirectionQueue = new ConcurrentBag<SignalLampDirectionModel>(signalLampDirectionList);

                foreach (UnpackingDirectionShowModel x in unpackings)
                {
                    x.UnpackingCount = result;
                    var UnpackModel = unpackingDirectionBLL.GetById(x.Id).ActivedObj;
                    UnpackModel.Amount = result;
                    unpackingDirectionBLL.Update(UnpackModel);
                    LogObj.Info("Pics更新数据->" + UnpackModel.SignalMark+",数量："+result);

                    var lsId = GolbalVariable.UnpackingDirectionModelBag.Where(s => s.UnpackingName == x.UnpackingName && s.DataType == "链数").FirstOrDefault();
                    var tsId = GolbalVariable.UnpackingDirectionModelBag.Where(s => s.UnpackingName == x.UnpackingName && s.DataType == "台数").FirstOrDefault();

                    var UnpackLs = unpackingDirectionBLL.GetById(lsId.Id).ActivedObj;
                    var UnpackTs = unpackingDirectionBLL.GetById(tsId.Id).ActivedObj;


                    //查找是否绑定信号灯，并根据信号灯设置的参数及当前状态判断信号灯的下一步状态
                    var signalLamps = signalLampDirectionQueue.Where(s => s.SignalMark == UnpackTs.UnpackingName && s.Enable == 1).ToList();
                    foreach (var signalLamp in signalLamps)
                    {
                        try
                        {
                            if (signalLamp.Enable == 0)
                                continue;
                            //单双链逻辑判断(0全部，1单数，2双数)
                            if (signalLamp.ChainMark == "1")
                            {
                                //单数链触发
                                //当前链数为双数时不触发
                                if (!Convert.ToBoolean(UnpackLs.Amount % 2))
                                {
                                    //返回不做处理
                                    continue;
                                }
                            }
                            else if (signalLamp.ChainMark == "2")
                            {
                                //双数链触发
                                //当前链数为单数时不触发
                                if (Convert.ToBoolean(UnpackLs.Amount % 2))
                                {
                                    //返回不做处理
                                    continue;
                                }
                            }
                            else if (signalLamp.ChainMark == "3")
                            {
                                //三链触发
                                if ((UnpackLs.Amount % 3) == 1 ? false : true)
                                {
                                    //返回不做处理
                                    continue;
                                }
                            }
                            //计算实际延迟台份
                            var realDelayCount = signalLamp.TriggerCount - UnpackTs.Amount;

                            if (signalLamp.LinkCount != UnpackLs.Amount)
                            {
                                signalLamp.DeliveryStatus = 0;
                            }

                            if (signalLamp.TriggerDelayCount > 0)
                            {
                                if (realDelayCount <= signalLamp.TriggerDelayCount && signalLamp.DeliveryStatus == 0)
                                {
                                    //正常出发
                                    signalLamp.Status = 2;
                                    signalLamp.RealDelayCount = "";
                                }
                                else if (realDelayCount > signalLamp.TriggerDelayCount && signalLamp.DeliveryStatus == 0)
                                {
                                    //延迟出发
                                    signalLamp.RealDelayCount = realDelayCount.ToString();
                                    signalLamp.Status = 1;
                                    RecordDelay(signalLamp, UnpackTs);
                                }
                                else
                                {
                                    //正常
                                    signalLamp.RealDelayCount = "";
                                    signalLamp.Status = 0;
                                }
                            }
                            else
                            {
                                if (realDelayCount == 0 && signalLamp.DeliveryStatus == 0)
                                {
                                    //正常出发
                                    signalLamp.Status = 2;
                                    signalLamp.RealDelayCount = "";
                                }
                                else if (realDelayCount > 0 && signalLamp.DeliveryStatus == 0)
                                {
                                    //延迟出发
                                    signalLamp.RealDelayCount = realDelayCount.ToString();
                                    signalLamp.Status = 1;
                                    RecordDelay(signalLamp, UnpackTs);
                                }
                                else
                                {
                                    //正常
                                    signalLamp.RealDelayCount = "";
                                    signalLamp.Status = 0;
                                }
                            }

                            signalLamp.Qty = UnpackTs.Amount;
                            signalLamp.LinkCount = UnpackLs.Amount;
                            var updatesql = string.Format("update LA_SignalLampDirection set DeliveryStatus={0},Status={1},RealDelayCount={2},Qty={3},LinkCount={4} where ID={5}", signalLamp.DeliveryStatus, signalLamp.Status, string.IsNullOrEmpty(signalLamp.RealDelayCount) ? "''" : signalLamp.RealDelayCount, signalLamp.Qty,signalLamp.LinkCount, signalLamp.ID);
                            signalLampDirectionBLL.UpdateNum(updatesql);
                            //signalLampDirectionBLL.Update(signalLamp);
                        }
                        catch (Exception ex)
                        {
                            LogObj.Error("84567->" + ex.ToString());
                        }
                    }
                }
            }
        }

        private void RecordDelay(SignalLampDirectionModel signalLamp,UnpackingDirectionModel unpacking)
        {
            //记录出发延迟信息
            PlanMonitorModel model1 = new PlanMonitorModel()
            {
                DelayState = 2,
                ProductName = signalLamp.LampName,
                AreaType = unpacking.UnpackingId,
                AreaName = unpacking.UnpackingName,
                Description = "出发延迟",
                StartTime = DateTime.Now,
                EndTime = DateTime.Now,
                WorkStartTime = DateTime.Now,
                WorkEndTime = DateTime.Now,
                CreateTime = DateTime.Now,
            };
            bllLogisticsSystemPlanMonitor.Add(model1);

        }

        /// <summary>
        /// TCP推送出发信息
        /// </summary>
        private async void TcpSend()
        {
            //循环查询数据库，如果有数据，则广播出去，并删除该信息
            while (!this.cancel_tcpSend.Token.IsCancellationRequested)
            {
                try
                {
                    var list = wmsDataBLL.GetModelList();
                    if (list != null && list.Count > 0)
                    {
                        foreach (var model in list)
                        {
                            WmsSendModel wmsSendModel = new WmsSendModel();
                            wmsSendModel.ProjectName = model.ProjectName;
                            wmsSendModel.LinkNumber = model.LinkNumber;
                            SendTcpData(wmsSendModel);
                            wmsDataBLL.Delete(model);
                            Thread.Sleep(100);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogObj.Error($"立库数据推送异常->{ex.ToString()}");
                }
                Thread.Sleep(1000);
            }
        }

        public void SendTcpData(WmsSendModel wmsDataModel)
        {
            SocketResponseModel socketResponseModel = new SocketResponseModel();
            socketResponseModel.Msg = "AnDongData";
            socketResponseModel.Status = "0";

            var data = CommonUtil.ToJson(wmsDataModel);
            socketResponseModel.Data = data;
            var json = CommonUtil.ToJson(socketResponseModel);

            string msg = "";
            // 向所有客户端发送消息
            tcpIpServer.SendToAllClient(json);
        }

        #region led屏开或关控制

        public void CloseAllLed()
        {
            var ledList = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").ToList();
            foreach (var setting in ledList)
            {
                CloseLed(setting,false);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="model"></param>
        /// <param name="isClose">是否记录关闭安东屏</param>
        public void CloseLed(SystemDeviceModel model,bool isClose)
        {
            try
            {
                if(isClose)
                    IsOpenLed(model, false);

                if (model.DeviceIpAddress != null)
                {
                    string ret = LEDDisplayDriver.Off(model.DeviceIpAddress);
                    string msg = "设置{" + model.DeviceIpAddress + "}关闭->" + ret;
                    LogObj.Info(msg);

                    if (ret.Contains("失败"))//失败重试5次
                    {
                        Thread thread = new Thread(() =>
                        {
                            for (int i = 0; i < 5; i++)
                            {
                                ret = LEDDisplayDriver.Off(model.DeviceIpAddress);
                                msg = "设置{" + model.DeviceIpAddress + "}关闭->" + ret;
                                LogObj.Info(msg);
                                if (ret.Contains("成功"))
                                {
                                    break;
                                }
                            }
                        });
                        thread.IsBackground = true;
                        thread.Start();
                    }
                }


            }
            catch (Exception ex)
            {
                LogObj.Error("关屏出错：" + ex.Message);
            }
        }

        /// <summary>
        /// 记录led打开状态
        /// </summary>
        /// <param name="model"></param>
        /// <param name="IsOpen"></param>
        private void IsOpenLed(SystemDeviceModel model, bool IsOpen)
        {

            Thread thread = new Thread(() =>
            {
                model.IsOpen = IsOpen;
                bllSystemDevice.Update(model);
            });
            thread.IsBackground = true;
            thread.Start();
        }

        public void OpenAllLed()
        {
            var ledList = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").ToList();
            foreach (var setting in ledList)
            {
                if(setting.IsOpen)//设置了开启才开
                    OpenLed(setting,false);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="model"></param>
        /// <param name="isOpen">是否记录开启安东屏</param>
        public void OpenLed(SystemDeviceModel model, bool isOpen=true)
        {
            try
            {
                if(isOpen)
                    IsOpenLed(model, true);

                if (model.DeviceIpAddress != null)
                {
                    string ret = LEDDisplayDriver.On(model.DeviceIpAddress);
                    string msg = "设置{" + model.DeviceIpAddress + "}打开->" + ret;
                    LogObj.Info(msg);
                    if (ret.Contains("失败"))//失败重试5次
                    {
                        Thread thread = new Thread(() =>
                        {
                            for (int i = 0; i < 5; i++)
                            {
                                ret = LEDDisplayDriver.On(model.DeviceIpAddress);
                                msg = "设置{" + model.DeviceIpAddress + "}打开->" + ret;
                                LogObj.Info(msg);
                                if (ret.Contains("成功"))
                                {
                                    break;
                                }

                            }
                        });
                        thread.IsBackground = true;
                        thread.Start();
                    }
                }


            }
            catch (Exception ex)
            {
                LogObj.Error("开屏屏出错：" + ex.Message);
            }
        }
        #endregion

    }
}
